package com.violet.service.manager.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.violet.domain.dto.manager.MProductDto;
import com.violet.domain.po.Category;
import com.violet.domain.po.Product;
import com.violet.domain.result.ResultPage;
import com.violet.domain.search.MyPage;
import com.violet.domain.search.manager.MProductSearch;
import com.violet.domain.vo.manager.MProductVo;
import com.violet.mapper.CategoryMapper;
import com.violet.mapper.ProductMapper;
import com.violet.service.manager.MProductService;
import com.violet.utils.FileUtils;
import com.violet.utils.SearchUtils;
import com.violet.utils.TransitionUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@RequiredArgsConstructor
public class MProductServiceImpl extends ServiceImpl<ProductMapper, Product>
        implements MProductService {

    private final CategoryMapper categoryMapper;

    private final TransitionUtils<MProductDto, Product, MProductVo> transition;

    private final FileUtils fileUtils;

    @Override
    public ResultPage<List<MProductVo>> search(MProductSearch search, MyPage myPage) {
        // 获取分页数据
        Page<Product> page = new Page<>(myPage.getCurrentPage(), myPage.getPageSize());
        baseMapper.selectPage(page, SearchUtils.getSearchWrapper(search));
        // 获取商品对应分类
        List<Category> categories = categoryMapper.selectList(
            new LambdaQueryWrapper<Category>()
                .in(
                    !page.getRecords().isEmpty(),
                    Category::getId,
                    page.getRecords().stream().map(Product::getCategoryId).collect(Collectors.toSet())
                )
        );
        // 构建 VO
        Stream<MProductVo> stream = page.getRecords().stream().map(item -> {
            // 构建 VO
            MProductVo vo = transition.transition(item, MProductVo.class);
            // 设置分类名称
            categories.stream()
                .filter(category -> category.getId().equals(item.getCategoryId()))
                .findFirst()
                .ifPresent(category -> vo.setCategoryName(category.getName()));
            // 返回 VO
            return vo;
        });
        // 返回结果
        return ResultPage.success(stream.toList(), "分页搜索", page);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ)
    public MProductVo create(MProductDto dto) {
        Product product = transition.transition(dto, Product.class);
        try {
            // 保存上传图片
            MultipartFile image = dto.getImage();
            String imageName = fileUtils.uploadImage(image);
            product.setImage(imageName);
            // 存储数据
            baseMapper.insert(product);
        }
        catch (DuplicateKeyException e) { throw new RuntimeException("商品名称重复"); }
        catch (DataIntegrityViolationException e) { throw new RuntimeException("商品分类不存在"); }
        catch (Exception e) { throw new RuntimeException("添加商品失败: " + e.getMessage()); }
        finally {
            // 数据未入库，但是图片已保存，删除图片
            if (
                product.getImage() != null &&
                !product.getImage().isEmpty() &&
                !baseMapper.exists(new LambdaQueryWrapper<Product>().eq(Product::getId, product.getId()))
            ) {
                fileUtils.deleteImage(product.getImage());
            }
        }
        // 更新分类商品数量（悲观锁）
        Category category = categoryMapper.getByIdWithPessimisticLock(product.getCategoryId());
        category.setProductCount(category.getProductCount() + 1);
        category.setUpdateTime(LocalDateTime.now());
        categoryMapper.updateById(category);
        // 返回
        return transition.transition(product, MProductVo.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ)
    public Boolean deleteById(String id) {
        // 获取商品
        Product product = baseMapper.getByIdWithPessimisticLock(id);
        if (product == null) throw new RuntimeException("商品不存在");
        // 删除商品记录
        int i = baseMapper.deleteById(product);
        if (i == 1) {
            // 更新分类的商品数量（悲观锁）
            Category category = categoryMapper.getByIdWithPessimisticLock(product.getCategoryId());
            category.setProductCount(category.getProductCount() - 1);
            category.setUpdateTime(LocalDateTime.now());
            categoryMapper.updateById(category);
            // 删除图片
            fileUtils.deleteImage(product.getImage());
            // 返回
            return true;
        }
        else throw new RuntimeException("删除商品失败");
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ)
    public MProductVo updateById(String id, MProductDto dto) {
        // 获取商品 (悲观锁)
        Product product = baseMapper.getByIdWithPessimisticLock(id);
        if (product == null) throw new RuntimeException("商品不存在");
        // 复制数据
        transition.copy(dto, product);
        try {
            // 图片不为空，则更新图片
            if (dto.getImage() != null) {
                fileUtils.deleteImage(product.getImage());
                String fileName = fileUtils.uploadImage(dto.getImage());
                product.setImage(fileName);
            }
            // 入库
            baseMapper.updateById(product);
        }
        catch (DuplicateKeyException e) { throw new RuntimeException("商品名称重复"); }
        catch (DataIntegrityViolationException e) { throw new RuntimeException("商品分类不存在"); }
        catch (Exception e) { throw new RuntimeException("添加商品失败: " + e.getMessage()); }
        // 返回
        Category category = categoryMapper.selectById(product.getCategoryId());
        return transition.transition(product, MProductVo.class).setCategoryName(category.getName());
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ)
    public MProductVo addTag(String id, String tags) {
        // 获取商品 (悲观锁)
        Product product = baseMapper.getByIdWithPessimisticLock(id);
        if (product == null) throw new RuntimeException("商品不存在");
        // 更新标签
        if (product.getTags() == null) product.setTags(tags);
        else  {
            String[] split = product.getTags().split(",");
            for (String s : split) {
                if (s.equals(tags)) {
                    throw new RuntimeException("标签已存在");
                }
            }
            // 标签长度不能大于 2
            if (split.length > 2) {
                throw new RuntimeException("标签至多只能有 2 个");
            }
            // 如果没有标签则直接添加，如果有则逗号隔开添加
            if (product.getTags().isEmpty()) product.setTags(tags);
            else product.setTags(product.getTags() + "," + tags);
        }
        // 入库
        product.setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(product);
        // 返回
        Category category = categoryMapper.selectById(product.getCategoryId());
        return transition.transition(product, MProductVo.class)
                .setCategoryName(category.getName());
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.REPEATABLE_READ)
    public MProductVo deleteTag(String id, String tags) {
        // 获取商品 (悲观锁)
        Product product = baseMapper.getByIdWithPessimisticLock(id);
        if (product == null) throw new RuntimeException("商品不存在");
        // 删除标签
        if (product.getTags() != null) {
            String[] split = product.getTags().split(",");
            for (int i = 0; i < split.length; i++) {
                if (split[i].equals(tags)) {
                    split[i] = split[split.length - 1];
                    split = Arrays.copyOf(split, split.length - 1);
                    product.setTags(String.join(",", split));
                    product.setUpdateTime(LocalDateTime.now());
                    baseMapper.updateById(product);
                    Category category = categoryMapper.selectById(product.getCategoryId());
                    return transition.transition(product, MProductVo.class)
                            .setCategoryName(category.getName());
                }
            }
        }
        return null;
    }

}
